#include <vtkForceDirectedLayoutStrategy.h>
#include <vtkGraphLayoutView.h>
#include <vtkMutableUndirectedGraph.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>

#include <vtkArcSource.h>
#include <vtkPolyDataMapper.h>
#include "vtkPolygon.h"
#include "vtkRegularPolygonSource.h"
#include "vtkEllipseArcSource.h"
#include "vtkProperty.h"
#include <vtkCubeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkCamera.h>
#include <vtkProperty.h>
#include <vtkPolyDataMapper.h>
#include "vtkPolygon.h"
#include "vtkProperty.h"
#include "vtkUnsignedCharArray.h"
#include "vtkMinimalStandardRandomSequence.h"
#include "vtkCellData.h"
#include "vtkTransform.h"
#include "vtkTransformPolyDataFilter.h"
#include "vtkAlgorithmOutput.h"
#include "vtkInteractorStyleTrackballCamera.h"
#include "vtkCoordinate.h"

namespace {

    struct RingObject {
        vtkSmartPointer<vtkPolyData> ringPolyData;
        vtkSmartPointer<vtkActor> ringActor;
    };

    struct RingPolyData {
        vtkSmartPointer<vtkPoints> allPoints;
        vtkSmartPointer<vtkCellArray> allPolygons;
    };

    static const int RowCount = 285;
    static const int ColCount = 460;
    static RingPolyData ringPolyData = {};

    void initRingPolyData() {
        const double RowDegree = vtkMath::Pi() * 2 / RowCount;
        const double Radius = 500;
        const double AreaAvgRadius = Radius / ColCount;
        const double ArcSegmentCount = 1;
        double ArcSegmentDegree = RowDegree / ArcSegmentCount;

        vtkNew<vtkPoints> allPoints;
        vtkNew<vtkCellArray> allPolygons;

        for (int i = 0; i < RowCount; ++i) {

            double degree = RowDegree * i;

            for (int j = 0; j < ColCount; ++j) {

                vtkNew<vtkPolygon> polygon;
                polygon->GetPointIds()->SetNumberOfIds((ArcSegmentCount + 1) * 2);
                vtkIdType numberOfPoints = allPoints->GetNumberOfPoints();


                double radius0 = j * AreaAvgRadius;
                double radius1 = (j + 1) * AreaAvgRadius;

                for (int k = 0; k < ArcSegmentCount + 1; ++k) {
                    double d0 = degree + (ArcSegmentDegree) * k;
                    double x0 = cos(d0) * radius0;
                    double y0 = sin(d0) * radius0;
                    allPoints->InsertNextPoint(x0, y0, 0.0);

                    double d1 = degree + (ArcSegmentDegree) * k;
                    double x1 = cos(d1) * radius1;
                    double y1 = sin(d1) * radius1;
                    allPoints->InsertNextPoint(x1, y1, 0.0);

                    polygon->GetPointIds()->SetId(k, numberOfPoints + (ArcSegmentCount - k));
                    polygon->GetPointIds()->SetId(k + ArcSegmentCount + 1, numberOfPoints + ArcSegmentCount + 1 + k);
                }

                allPolygons->InsertNextCell(polygon);
            }
        }

//    qDebug() << "initRingPolyData end";

        RingPolyData data;
        data.allPolygons = allPolygons;
        data.allPoints = allPoints;

//    PPIHelper::ringPolyData = data;
        ringPolyData = data;
    }

    //createRing
    RingObject createRing() {

        vtkNew<vtkPolyData> polygonPolyData;
        polygonPolyData->SetPoints(ringPolyData.allPoints);
        polygonPolyData->SetPolys(ringPolyData.allPolygons);

        vtkNew<vtkPolyDataMapper> mapper;
        mapper->SetInputData(polygonPolyData);

        vtkNew<vtkActor> actor;
        actor->SetMapper(mapper);

        vtkNew<vtkNamedColors> colors;
//    actor->GetProperty()->SetColor(colors->GetColor4d("Tomato").GetData());

        RingObject ringObject;
        ringObject.ringActor = actor;
        ringObject.ringPolyData = polygonPolyData;
        return ringObject;
    }

}

int main(int, char *[]) {
    initRingPolyData();
    cout << "ringPolyData" << "";


    vtkNew<vtkNamedColors> colors;


    vtkNew<vtkRenderer> renderer;

    vtkNew<vtkRenderWindow> renderWindow;
    renderWindow->SetSize(700, 1600);
    renderWindow->AddRenderer(renderer);
    renderWindow->SetWindowName("Cylinder");

    vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
    renderWindowInteractor->SetRenderWindow(renderWindow);


    vtkSmartPointer<vtkCamera> camera = renderer->GetActiveCamera();
    renderer->ResetCamera();

////    camera 1
////    camera->SetPosition(0, 0, 1);
//    camera->SetFocalPoint(0, 0, 0);
//    camera->SetViewUp(0, 1, 0);
////    camera->SetParallelScale(3500.0);
////    camera->ParallelProjectionOn();
//    camera->SetPosition(0, 0, 4000);

    // camera 2
    camera->SetPosition(0, 0, 1);
    camera->SetFocalPoint(0, 0, 0);
    camera->SetViewUp(0, 1, 0);
    camera->SetParallelScale(1000.0);
    camera->ParallelProjectionOn();



    vtkNew<vtkCubeSource> cube;
    vtkNew<vtkPolyDataMapper> cubeMapper;
    cubeMapper->SetInputConnection(cube->GetOutputPort());
    vtkNew<vtkActor> actorCube;
    cube->SetBounds(-1, 1, -1, 1, -1, 1);
    actorCube->SetMapper(cubeMapper);
    renderer->AddActor(actorCube);




    // 从底部向上排列
    int eCount = 9;
    for (int i = 0; i < eCount; ++i) {
        RingObject ringObject = createRing();
        vtkSmartPointer<vtkActor> ringActor = ringObject.ringActor;
        renderer->AddActor(ringActor);

        // 关闭对光的反射和漫反射
        ringActor->GetProperty()->SetAmbient(1.0);
        ringActor->GetProperty()->SetDiffuse(0);
        ringActor->GetProperty()->SetSpecular(0);
        ringActor->GetProperty()->ShadingOff();

//        ringActor->RotateX(-60);
//        ringActor->SetPosition(0, (i - eCount * 0.5) * 200.0 + 150.0, 0);

        double size = 500;
        double y = -(eCount - i) * size * 2 + size + eCount * size;
        ringActor->SetPosition(0, y, 0);

        vtkSmartPointer<vtkCoordinate> normCoords = vtkSmartPointer<vtkCoordinate>::New();
        normCoords->SetCoordinateSystemToView();


        if (i == 0) {
            double b[6];
            ringActor->GetBounds(b);
            std::cout << b[0] << "-" << b[1] <<  "-" << b[2] <<  "-" << b[3] <<  "-" << b[4] <<  "-" << b[5];
        }

    }





    renderWindow->Render();
    renderWindowInteractor->Start();

    return EXIT_SUCCESS;
}